﻿/** InputManager.cs - A simple Game Input Manager
 * @author - Spencer Evans
 * @attribution - Similar to a class found in the Roleplaying starter pack On the 
 *  XNA Creators Club Online website (http://creators.xna.com).
 */


#region Using Statements
// .Net
using System;
using System.Collections.Generic;

// XNA
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

// Solstice
#endregion


namespace BenevolentSun
{
    using ActionMapKey = System.Int32;

    /** The InputManager handles maintianing all input from 
     * the keyboard.
     */
    public static class InputManager
    {

        #region Enumerations
        public enum MouseButtons
        {
            Left,
            Middle,
            Right,
            XButton1,
            XButton2
        }
        #endregion


        #region Properties
        private static Dictionary<PlayerIndex, GamePadState> currentGamePadState = new Dictionary<PlayerIndex, GamePadState>();

        private static Dictionary<PlayerIndex, GamePadState> previousGamePadState = new Dictionary<PlayerIndex, GamePadState>();


        /** Gets the game that this manager is running in. */
        private static Game game;
        public static Game Game
        {
            get { return game; }
            internal set { game = value; }
        }

        #region Keyboard Data
        /** The state of the keyboard as of the last update. */
        private static KeyboardState currentKeyboardState;
        public static KeyboardState CurrentKeyboardState
        {
            get { return currentKeyboardState; }
        }
        /** The state of the keyboard before the last update. */
        private static KeyboardState previousKeyboardState;
        public static KeyboardState PreviousKeyboardState
        {
            get { return previousKeyboardState; }
        }
        /** Helper to see if a key is currently pressed. */
        public static bool IsKeyPressed(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key);
        }
        /** Helper to see if a key was pressed last update. */
        public static bool WasKeyPressed(Keys key)
        {
            return previousKeyboardState.IsKeyDown(key);
        }
        /** Check if a key was just pressed in the most recent update. */
        public static bool IsNewKeyPress(Keys key)
        {
            return (currentKeyboardState.IsKeyDown(key)) &&
                (!previousKeyboardState.IsKeyDown(key));
        }
        /** Check if a key was just released in the most recent update. */
        public static bool IsNewKeyRelease(Keys key)
        {
            return (!currentKeyboardState.IsKeyDown(key)) &&
                (previousKeyboardState.IsKeyDown(key));
        }
        #endregion


        #region Mouse Data
        /** The state of the mouse as of the last update. */
        private static MouseState currentMouseState;
        public static MouseState CurrentMouseState
        {
            get { return currentMouseState; }
        }
        /** The state of the mouse before the last update. */
        private static MouseState previousMouseState;
        public static MouseState PreviousMouseState
        {
            get { return previousMouseState; }
        }
        /** Checks to see if the mouse moved. */
        public static bool IsMouseMove()
        {
            return !((currentMouseState.X == previousMouseState.X) && (currentMouseState.Y == previousMouseState.Y));
        }
        /** Gets the current mouse move distance. */
        public static Vector2 GetMouseMove()
        {
            return new Vector2((currentMouseState.X - previousMouseState.X), (currentMouseState.Y - previousMouseState.Y));
        }
        /** Checks to see if the mouse wheel scrolled. */
        public static bool IsMouseWheelScroll()
        {
            return !(currentMouseState.ScrollWheelValue == previousMouseState.ScrollWheelValue);
        }
        /** Gets the current mouse move distance. */
        public static int GetMouseWheelScroll()
        {
            return (currentMouseState.ScrollWheelValue - previousMouseState.ScrollWheelValue);
        }
        /** Checks to see if a mouse button is pressed. */
        public static bool IsMouseButtonPressed(MouseButtons button)
        {
            MouseState state = currentMouseState;

            switch (button)
            {
                case MouseButtons.Left:
                    return (state.LeftButton == ButtonState.Pressed);
                case MouseButtons.Middle:
                    return (state.MiddleButton == ButtonState.Pressed);
                case MouseButtons.Right:
                    return (state.RightButton == ButtonState.Pressed);
                case MouseButtons.XButton1:
                    return (state.XButton1 == ButtonState.Pressed);
                case MouseButtons.XButton2:
                    return (state.XButton2 == ButtonState.Pressed);
                default:
                    throw new ArgumentException("button");
            }
        }
        /** Checks to see if a mouse button is pressed. */
        public static bool WasMouseButtonPressed(MouseButtons button)
        {
            MouseState state = previousMouseState;

            switch (button)
            {
                case MouseButtons.Left:
                    return (state.LeftButton == ButtonState.Pressed);
                case MouseButtons.Middle:
                    return (state.MiddleButton == ButtonState.Pressed);
                case MouseButtons.Right:
                    return (state.RightButton == ButtonState.Pressed);
                case MouseButtons.XButton1:
                    return (state.XButton1 == ButtonState.Pressed);
                case MouseButtons.XButton2:
                    return (state.XButton2 == ButtonState.Pressed);
                default:
                    throw new ArgumentException("button");
            }
        }
        /** Check if a button was just pressed in the most recent update. */
        public static bool IsNewMouseButtonPress(MouseButtons button)
        {
            return (InputManager.IsMouseButtonPressed(button)) &&
                (!InputManager.WasMouseButtonPressed(button));
        }
        /** Check if a button was just released in the most recent update. */
        public static bool IsNewMouseButtonRelease(MouseButtons button)
        {
            return (!InputManager.IsMouseButtonPressed(button)) &&
                (InputManager.WasMouseButtonPressed(button));
        }
        /** Indicates if the mouse should be locked to the game window (when active) or not. */
        private static bool clampMouse = false;
        public static bool ClampMouse
        {
            get { return clampMouse; }
            set { clampMouse = value; }
        }

        public static Point GetCurrentMousePosition()
        {
            return new Point(currentMouseState.X, currentMouseState.Y);
        }
        #endregion

        #endregion


        #region Initialization
        public static void Initialize(Game game)
        {
            Game = game;

            currentGamePadState.Add(PlayerIndex.One, GamePad.GetState(PlayerIndex.One));
            currentGamePadState.Add(PlayerIndex.Two, GamePad.GetState(PlayerIndex.Two));
            currentGamePadState.Add(PlayerIndex.Three, GamePad.GetState(PlayerIndex.Three));
            currentGamePadState.Add(PlayerIndex.Four, GamePad.GetState(PlayerIndex.Four));
            previousGamePadState.Add(PlayerIndex.One, currentGamePadState[PlayerIndex.One]);
            previousGamePadState.Add(PlayerIndex.Two, currentGamePadState[PlayerIndex.Two]);
            previousGamePadState.Add(PlayerIndex.Three, currentGamePadState[PlayerIndex.Three]);
            previousGamePadState.Add(PlayerIndex.Four, currentGamePadState[PlayerIndex.Four]);
        }
        #endregion


        #region Update Methods
        /** Polls the keyboard for the current input state.
         */
        public static void Update(GameTime gameTime)
        {
            // Update the game pad state
            UpdateGamepad(gameTime);

            // Update the keyboard state
            previousKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            // If the mouse is to be clamped, do it now! then update its state info
            if (Game.IsActive && ClampMouse)
            {
                int x = Mouse.GetState().X;
                int y = Mouse.GetState().Y;
                Viewport vp = Game.GraphicsDevice.Viewport;
                x = (int)MathHelper.Clamp(x, vp.X, vp.X + vp.Width);
                y = (int)MathHelper.Clamp(y, vp.Y, vp.Y + vp.Height);
                Mouse.SetPosition(x, y);
            }
            previousMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();

            ActionMap.Update(gameTime);
        }

        static void UpdateGamepad(GameTime gameTime)
        {
            previousGamePadState[PlayerIndex.One] = currentGamePadState[PlayerIndex.One];
            previousGamePadState[PlayerIndex.Two] = currentGamePadState[PlayerIndex.Two];
            previousGamePadState[PlayerIndex.Three] = currentGamePadState[PlayerIndex.Three];
            previousGamePadState[PlayerIndex.Four] = currentGamePadState[PlayerIndex.Four];

            currentGamePadState[PlayerIndex.One] = GamePad.GetState(PlayerIndex.One);
            currentGamePadState[PlayerIndex.Two] = GamePad.GetState(PlayerIndex.Two);
            currentGamePadState[PlayerIndex.Three] = GamePad.GetState(PlayerIndex.Three);
            currentGamePadState[PlayerIndex.Four] = GamePad.GetState(PlayerIndex.Four);
        }
        #endregion


        #region public Methods
        /** Forces a manual refreshing of the current mouse state, this is really meant for cursor
         * since it may alter the mouse position, but it is left totally public so if any other 
         * code updates the mouse position, it can also refresh the input manager state.
         */
        public static void SetMousePosition(int x, int y)
        {
            Mouse.SetPosition(x, y);
            currentMouseState = Mouse.GetState();
        }
        #endregion

        /** Check if a key was just pressed in the most recent update. */
        public static GamePadState GamePadState(PlayerIndex playerIndex)
        {
            return currentGamePadState[playerIndex];
        }

        public static GamePadState PreviousGamePadState(PlayerIndex playerIndex)
        {
            return previousGamePadState[playerIndex];
        }

        public static bool IsNewButtonPress(PlayerIndex playerIndex, Buttons button)
        {
            return (currentGamePadState[playerIndex].IsButtonDown(button)) &&
                (!previousGamePadState[playerIndex].IsButtonDown(button));
        }

        public static bool IsButtonPress(PlayerIndex playerIndex, Buttons button)
        {
            return currentGamePadState[playerIndex].IsButtonDown(button);
        }

        public static GamePadThumbSticks Thumbsticks(PlayerIndex playerIndex)
        {
            return currentGamePadState[playerIndex].ThumbSticks;
        }

        public static GamePadTriggers Triggers(PlayerIndex playerIndex)
        {
            return currentGamePadState[playerIndex].Triggers;
        }

    }
}
